Girder COM

 

 

The addition of LuaCOM  extensions opens a whole new world to Girder.  Component Object Model (COM) is a specification that enables the creation of software components that are language and location independent.  It is a Microsoft technology that is also includes other terms such as OLE, DCOM and ActiveX.  This allows Girder to use software components to develop more sophisticated applications.  Girder can use COM objects to retrieve information from many sources, to interact with users, to receive events from objects, and to control other applications.  Any user of Visual Basic, C, VBScript, or WScript will likely be familiar with COM.  Using COM objects in Lua is very similar to the WScript and VBScript languages.  . 

 

The LuaCOM homepage is http://www.tecgraf.puc-rio.br/~rcerq/luacom/.  Documentation is available online from this page.

 

Useful resources on MSDN:

 

MSDN (Microsoft Developer Network): http://msdn.microsoft.com/

 

COM: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/htm/registry_6lrr.asp

 

FileSystemObject: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/jsfsotutor.asp

 

WindowsScriptingHost: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/jsfsotutor.asp

 

Scripting Guide: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnclinic/html/scripting01142003.asp

 

Windows Management and Instrumentation: http://msdn.microsoft.com/library/en-us/dnanchor/html/anch_wmi.asp

 

Managing Windows with WMI: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmi/html/mngwmi.asp

 

XML Core Services: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/htm/dom_starter_0h7x.asp

 

WinHTTP: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winhttp/http/winhttp_reference.asp

 

Other resources:

 

Win32 Scripting Tools: http://cwashington.netreach.net/main/tools

 

The Windows Scripting Guide: http://www.winguides.com/scripting/

 

Replace API Calls Using WMI: http://www.fawcette.com/Archives/premier/mgznarch/vbpj/2000/09sep00/kg0009/kg0009.asp

 

Fops ActiveX Control: http://www.softuarium.com/fops.htm

 

ActiveX Components (free): http://www.sevillaonline.com/ActiveX/

 


Loading LuaCOM

 

The LuaCOM library must be loaded before use.  This is accomplished by using the following statement:

 

LoadLuaCom ()

 

This function returns 0 or > if successful. 

 

Using LuaCOM

 

To program using ActiveX and COM objects requires some understanding of how they work.  In short, objects are initialized, then used and then disposed of before the program terminates.  Objects are created using the:

 

MyObject = luacom.CreateObject (“MyObject.Object”) or

 

MyObject = luacom.GetObject (MyObject.Object”) functions. 

 

These functions return a table value which is the interface to the object.

 

Object properties are retrieved using the following syntax:

 

MyObject.value

 

Methods are executed using in a similar fashion but with colon “:” not a period “.”

 

MyObject:method ()

 

Objects that generate events can also be used.  See example 7 below for how to create an event handler.  Objects are enumerated differently then in VBScript or WScript.  See example 2 for how to enumerate WMI objects.

 

It is imperative that objects be disposed of properly.  So when you are finished with an object:

 

MyObject = nil

Collectgarbage ()

 

Lastly, the Girder programming environment allows you to make changes to the code while it is running.  In most instances, this fine but when using LuaCOM, this can lead to a Girder crash.  To avoid this, you must not change your program until you have disposed of all objects.  This particularly true when using event handlers.  To deal with this, Girder 3.3 now includes a BeforeScriptReset event.  This event occurs when the F11 key is pressed (Reset Script).  This event and the OnGirderClose event should be linked to a section of Lua code the makes sure any existing objects are properly disposed of.

 

Closing LuaCOM

 

The LuaCOM library should be closed when a program finishes.  It does not need to be closed after each use, just prior to Girder exiting.  The command is:

 

UnloadLuaCom ()

 

This is easily done by linking the OnGirderClose event to the above Lua code.

 

 

 

 

 

Examples

 

1. FileSystemObject.  See the MSDN reference for object details.

 

fso = luacom.CreateObject ("Scripting.FileSystemObject")

 

fso:FileExists ("\\\\C:\\test.txt") == 1 then

  print (“File Exists.”)

end

fso = nil

collectgarbage ()

 

Notes: In Lua, the “\” is an escape character and double “\\” equal one “\”.  COM Objects use the C conventions for true and false (1,0) where as LUA uses non-nil and nil.

 

 

2. Windows Management and Instrumentation.  WMI is a very powerful way to monitor and control Windows 2000 and greater computers.  It is not easy to understand and using it will require a significant amount of reading on MSDN.  Here is an example that enumerates all the hard drives on a system and saves that information in a text file.

 

fs = luacom.CreateObject("Scripting.FileSystemObject")

 

outFile = fs:CreateTextFile("c:\\harddrive.txt", 1)

 

diskdrives = luacom.GetObject("winmgmts:{impersonationLevel=impersonate}"):InstancesOf("Win32_DiskDrive")

 

diskdrives_en = luacom.GetEnumerator(diskdrives)

Item = diskdrives_en:Next()

 

while Item do

  outFile:WriteLine("Caption: "..Item:Caption())

  outFile:WriteLine("Description: "..Item:Description())

  outFile:WriteLine("InterfaceType: "..Item:InterfaceType())

  outFile:WriteLine("Manufacturer: "..Item:Manufacturer())

  outFile:WriteLine("Partitions: "..Item:Partitions())

  outFile:WriteLine("ScsiBus: "..Item:ScsiBus())

  outFile:WriteLine("ScsiTargetID: "..Item:ScsiTargetID())

  outFile:WriteLine("Size: "..Item:Size())

  outFile:WriteLine("")

 

  Item = diskdrives_en:Next()

end

 

outFile:Close()

 

fs = nil

diskdrives = nil

diskdrives_en = nil

collectgarbage ()

 

3. DOM (Document Object Model) and XML for retrieving data from CoolMon (http://coolmon.arsware.org/)

 

DOM = luacom_CreateObject("Msxml2.DOMDocument")

DOM.async = 0

DOM:load ("http://localhost:61300")

proc = DOM:selectSingleNode ("CoolMon/Raw/Processor_Usage")

DOM = nil

collectgarbage ()

 

print ("Processor Usage: ",proc.text)

 

4. Windows Scripting Host – Popup Message

 

function PopupMessage (Text,SecondsToWait,Title,Type)

-- see MSDN Wscript.Shell Popup for documentation

  local WshShell,x

  WshShell = luacom_CreateObject("WScript.Shell")

  x = WshShell:Popup(Text,SecondsToWait,Title,Type)

  WshShell = nil

  collectgarbage ()

  return x

end

 

PopupMessage ("Welcome to LuaCOM”, 20, "Girder", 64)

 

5. Using WMI to start a process.

 

function WMICreateProcess (strComputer,Process)

  local objWMIService,objProcess,result

  strComputer = strComputer or "."

  objWMIService = luacom.GetObject ( "winmgmts:{impersonationLevel=Impersonate}!\\\\" ..strComputer.."\\root\\cimv2")

  objProcess = objWMIService:Get("Win32_Process")

  result = objProcess:Create (Process,nil,nil,nil)

  objProcess = nil

  objWMIService = nil

  collectgarbage ()

  return result

end

 

WMICreateProcess (".","NotePad.exe")

 

6. Get Computer Name using WSH

 

function GetComputerName ()

  local WshNetwork,x

  WshNetwork = luacom_CreateObject("WScript.Network")

  x = WshNetwork.ComputerName

  WshNetwork = nil

  collectgarbage ()

  return x

end

 

print (GetComputerName())

 

7. Below is a complete example of using LuaCOM with CoolMon.  This example uses asynchronous retrieval of CoolMon data by connecting an event handler with the DOM object.

 

-- CoolMon system information retrieval

 

-- retrieves data from CoomMon via its web service

-- can retreive data from any machine on the network

-- requires coolmon v2

-- retrieves data async, use luacom events!

 

function CoolMonInit ()

  CoolMon = {} -- table of CM DOMs,Data

  print ("CoolMon Initialized")

end

 

function CoolMonDisconnect ()

  local i,j

  for i,j in CoolMon do

    CoolMon [i].DOM= nil

  end

  collectgarbage ()

end

 

CoolMonEvent = {}

function CoolMonEvent:onreadystatechange  ()

  local i,j

  for i,j in CoolMon do

    if CoolMon [i].pendingload and CoolMon [i].DOM.readyState == 4 then -- dom is loaded

      CoolMonDataReady (i)

    end

  end

end

 

function CoolMonAddLocation (url,async,datareadyevent)

-- create XML DOM object

-- async, 0 = no, 1 = yes,  if a remote connection, probably should use async connections ...

-- datareadyevent - event to call when new data has been received

-- title = name of resource

  CoolMon [url] = {}

  CoolMon [url].DOM = luacom_CreateObject("Msxml2.DOMDocument")

  CoolMon [url].DOM.async = 1--async or 1 -- allow async  as default

  CoolMon [url].datareadyevent = datareadyevent

  CoolMon [url].pendingload = nil -- non nil when waiting for return event

  CoolMon [url].data = {} -- retrieved data

  if not luacom_Connect(CoolMon [url].DOM,CoolMonEvent) then --link event handler

    print ("CoolMon: Unable to connect onreadystatechange")

   else

     print ("CoolMon: ",url," added.")

  end

end

 

 

 

function CoolMonLoadData (url)

-- load data from coolmon

  if CoolMon [url].DOM.readyState == 4 then  -- do not call to load data if previous load operation not complete

    CoolMon [url].pendingload = 0 -- for event handler below

    return CoolMon [url].DOM:load (url)

  end

end

 

function CoolMonDataReady (url)

-- get data

  local startingnode

  CoolMon [url].pendingload = nil

 

  if CoolMon [url].DOM.readyState == 4 then -- dom is loaded

    startingnode = CoolMon [url].DOM:selectSingleNode ("CoolMon")

    if CoolMon [url].DOM.parseError.errorCode ~= 0 then    -- error parsing/retrieving

      CoolMon [url].data = {}

      CoolMon [url].data ["Error Code"] = CoolMon [url].DOM.parseError.errorCode

      CoolMon [url].data ["Reason"] = CoolMon [url].DOM.parseError.reason

     else

        CoolMon [url].data = CoolMonDatatoTable (startingnode, 0)

    end

    if CoolMon [url].datareadyevent then

      CoolMon [url].datareadyevent (url)

    end

  end

end

 

function CoolMonDatatoTable (StartNode)

-- parse CoolMon XML data into a table

-- this is the quick and dirty but done this way for speed.

  local i,t,dn,nl,xNode

 

  t = {}

  dn = StartNode:selectSingleNode ("Raw")

  if dn:hasChildNodes () > 0 then

    nl = dn.childNodes

    for i = 0, nl.length - 1 do

      xNode = nl:item (i)

      t [xNode.nodeName] = xNode.text

    end

  end

  dn = StartNode:selectSingleNode ("Formatted")

  if dn:hasChildNodes () > 0 then

    nl = dn.childNodes

    for i = 0, nl.length - 1 do

      xNode = nl:item (i)

      if strfind (xNode.text,":") then

        t [strsub (xNode.text,1,strfind (xNode.text,":") -1)] = strsub (xNode.text,strfind (xNode.text,":") +1,-1)

      end

    end

  end

  return t

end

 

function CoolMonDataPrint (data)

  local k,v

  for k,v in data do

    print ("Index: "..k.."  Value: "..v)

  end

end

 

The above examples just give a token idea of what can be done with LuaCOM. 

 

Girder COM Object

 

Girder provides an COM object to allow other programs to directly send events to Girder.  Here is an example from WScript.


GirderEvent = WScript.CreateObject("Girder.GirderEvent");
GirderEvent.Device = 18;
GirderEvent.EventString = "MyEvent";
GirderEvent.Payload(1) = payload;
GirderEvent.Send();